package cn.cxyxj.code_auth_jwt_persistence_prod.config;

import cn.cxyxj.code_auth_jwt_persistence_prod.auth.service.impl.RedisClientDetailsService;
import cn.cxyxj.code_auth_jwt_persistence_prod.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;


@EnableAuthorizationServer
@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    UserDetailsServiceImpl userService;

    @Autowired
    DataSource dataSource;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 注入 authenticationManager 来支持 password grant type，如果是从数据库查询用户信息，还需要指定 UserDetailsService
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    private String SIGNING_KEY = "cxyxj-key";
    /**
     * 配置 Token 存储位置
     * @return
     */
    @Bean
    JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new CustomAdditionalInformation();
        converter.setSigningKey(SIGNING_KEY);
        return converter;
    }

    @Bean
    TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /**
     * @param security：授权服务器安全配置器
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 检查令牌访问放行
        security.checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
        // allowFormAuthenticationForClients 设置为 true，会注入 ClientCredentialsTokenEndpointFilter
        // 该过滤器允许client_id + client_secret 放在
    }

    /**
     * @param clients：客户端的详细配置信息
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        RedisClientDetailsService redisClientDetailsService = redisClientDetailsService();
        clients.withClientDetails(redisClientDetailsService);
        redisClientDetailsService.loadAllClientToCache();
    }

    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("123456"));
    }



    /**
     * @param endpoints：授权服务器端点配置器
     * 配置令牌的访问服务和令牌服务
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices())
                .tokenStore(tokenStore())
                .userDetailsService(userService);
                // clientDetailsService 可以不用设置，会在 AuthorizationServerEndpointsConfiguration 类中自动设置
                //.setClientDetailsService(clientDetailsService);

        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        // 谁先添加谁先执行，两者添加顺序会导致 token 信息不一致
        //tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customAdditionalInformation,jwtAccessTokenConverter()));
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter()));
        endpoints.tokenEnhancer(tokenEnhancerChain);
    }





    /**
     * 声明 ClientDetails 实现
     */
    @Bean
    public RedisClientDetailsService redisClientDetailsService() {
        RedisClientDetailsService redisClientDetailsService = new RedisClientDetailsService(dataSource);
        redisClientDetailsService.setRedisTemplate(redisTemplate);
        return redisClientDetailsService;
    }

    @Bean
    public RandomValueAuthorizationCodeServices authorizationCodeServices() {
//        RedisAuthorizationCodeServices redisAuthorizationCodeServices = new RedisAuthorizationCodeServices();
//        redisAuthorizationCodeServices.setRedisTemplate(redisTemplate);
//        return redisAuthorizationCodeServices;
        return new JdbcAuthorizationCodeServices(dataSource);
    }


}
